/*
 *	DjoltToken.m
 *
 *	David Janes
 *	Discover Anywhere Mobile
 *	March 2010
 *
 *	Copyright 2010 Discover Anywhere Mobile
 */

#import "DjoltTemplate.h"
#import "DjoltNode.h"
#import "DjoltContext.h"
#import "DjoltFilter.h"
#import "DjoltToken.h"

#import <RegexKit/RegexKit.h>

static NSString* token_re = 
	@"    ("
	@"        \"(?P<q1>([^\"\\\\]|\\\\.)*)\""
	@"        |"
	@"        '(?P<q2>([^\\\\]|\\\\.)*)'"
	@"        |"
	@"        (?P<a1>[^\\s|]+)"
	@"    )"
	@"	(?P<filters>("
	@"    [|]"
	@"    ([a-z][_a-z0-9]+)"
	@"    ("
	@"        :\"(([^\"\\\\]|\\\\.)*)\""
	@"        |"
	@"        :'(([^\\\\]|\\\\.)*)'"
	@"        |"
	@"        :([^\\s|]+)"
	@"    )?"
	@"	)*)"
	@"";
static RKRegex* token_rex = nil;

static NSString* filter_re = 
	@"    [|]"
	@"    (?P<filter>[a-z][_a-z0-9]+)"
	@"    ("
	@"        :\"(?P<a1>([^\"\\\\]|\\\\.)*)\""
	@"        |"
	@"        :'(?P<a2>([^\\\\]|\\\\.)*)'"
	@"        |"
	@"        :(?P<a3>[^\\s|]+)"
	@"    )?";
static RKRegex* filter_rex = nil;

@implementation DjoltToken
@synthesize tvalue;
@synthesize filters;

#define IF_LOG if (0) IF_LOG

- (void) _parse_filters:(NSString*)_filter_str
{
	if (filter_rex == nil) {
		filter_rex = [[RKRegex 
			regexWithRegexString:filter_re
			options:RKCompileCaseless|RKCompileMultiline|RKCompileDotAll|RKCompileExtended
		] retain];
	}

	NSMutableArray* fs = [[NSMutableArray alloc] initWithCapacity:0];

	IF_LOG(@"_parse_filters: START: _filter_str=%@", _filter_str);

	RKEnumerator* filter_e = [_filter_str matchEnumeratorWithRegex:filter_rex];
	while ([filter_e nextRanges] != NULL) {
		NSRange e_range = [filter_e currentRange];

		id g_filter = nil;
		[filter_e getCapturesWithReferences:@"${filter}", &g_filter, nil];

		id g_argument = nil;
		if (!g_argument) [filter_e getCapturesWithReferences:@"${a1}", &g_argument, nil];
		if (!g_argument) [filter_e getCapturesWithReferences:@"${a2}", &g_argument, nil];
		if (!g_argument) [filter_e getCapturesWithReferences:@"${a3}", &g_argument, nil];

		IF_LOG(@"_parse_filters: FILTER=%@ argument=%@", g_filter, g_argument);

		[fs addObject:[[DjoltFilter alloc] initWithFilter:g_filter argument:g_argument]];
	}

	self.filters = fs;
}

+ (NSArray*) tokenize:(NSString*)_str
{
	if (token_rex == nil) {
		token_rex = [[RKRegex 
			regexWithRegexString:token_re
			options:RKCompileCaseless|RKCompileMultiline|RKCompileDotAll|RKCompileExtended|RKCompileDupNames
		] retain];
	}

	NSMutableArray* result = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];

	IF_LOG(@"tokenize: START: _str=%@", _str);
	RKEnumerator* token_e = [_str matchEnumeratorWithRegex:token_rex];
	while ([token_e nextRanges] != NULL) {
		IF_LOG(@"token: NEXT=%@", token_e.string);

		/*
		 */
		id g_q1 = nil;
		[token_e getCapturesWithReferences:@"${q1}", &g_q1, nil];

		id g_q2 = nil;
		[token_e getCapturesWithReferences:@"${q2}", &g_q2, nil];

		id g_a1 = nil;
		[token_e getCapturesWithReferences:@"${a1}", &g_a1, nil];

		IF_LOG(@"tokenize: TOKEN: g_q1=%@ g_q2=%@ g_a1=%@", g_q1, g_q2, g_a1);
		DjoltToken* token = nil;
		if (g_a1) {
			token = [[DjoltToken alloc] initWithAtom:g_a1];
		} else if (g_q1) {
			token = [[DjoltToken alloc] initWithQuote:g_q1];
		} else if (g_q2) {
			token = [[DjoltToken alloc] initWithQuote:g_q2];
		}

		/*
		 */
		id g_filters = nil;
		[token_e getCapturesWithReferences:@"${filters}", &g_filters, nil];

		IF_LOG(@"tokenize: FILTERS: %@", g_filters);

		if (g_filters) {
			[token _parse_filters:g_filters];
		}

		/*
		 */
		if (token) {
			[result addObject:token];
		}
	}

	return	result;
}

- (id) initWithAtom:(NSString*)_atom
{
    if (self = [super init]) {
		self->ttype = DjoltTokenTypeAtom;
		tvalue = _atom;
		filters = [[NSArray alloc] init];
	}

	return	self;
}

- (id) initWithQuote:(NSString*)_quote
{
    if (self = [super init]) {
		self->ttype = DjoltTokenTypeQuote;
		tvalue = _quote;
		filters = [[NSArray alloc] init];
	}

	return	self;
}

- (NSString*) render:(DjoltContext*)_context escape:(BOOL)_must_escape
{
	NSString* v = nil;

	if (self->ttype == DjoltTokenTypeQuote) {
		v = self.tvalue;
	} else {
		v = [_context getString:self.tvalue otherwise:nil];
	}

	IF_LOG(@"HERE:BEFORE v=%@ tvalue=%@ filters=%@", v, self.tvalue, filters);

	if ([filters count]) {
		for (DjoltFilter* filter in filters) {
			IF_LOG(@"FILTER=%@", filter.filter);
			if ([filter.filter compare:@"escape"] == 0) {
				_must_escape = YES;
			} else if ([filter.filter compare:@"safe"] == 0) {
				_must_escape = NO;
			} else {
				v = [filter filter:v context:_context];
			}
		}
	}

	IF_LOG(@"HERE:AFTER v=%@ tvalue=%@ filters=%@", v, self.tvalue, filters);

	if (v) {
		if (_must_escape) {
			v = [v stringByReplacingOccurrencesOfString:@"&" withString:@"&amp;"];
			v = [v stringByReplacingOccurrencesOfString:@"<" withString:@"&lt;"];
			v = [v stringByReplacingOccurrencesOfString:@">" withString:@"&gt;"];
			v = [v stringByReplacingOccurrencesOfString:@"'" withString:@"&#39;"];
			v = [v stringByReplacingOccurrencesOfString:@"\"" withString:@"&quot;"];
		}
	}

	return	v;
}
@end
